﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;

namespace EmbeddedSharp.Core
{
    /// <summary>
    /// Default function manager
    /// </summary>
    public class DefaultFunctionManager : IFunctionManager
    {
        protected readonly DefaultFunctionLoader dfl;

        /// <summary>
        /// Default constructor for initilization joeb
        /// </summary>
        public DefaultFunctionManager()
        {
            Functions = new Dictionary<string, object>();
            dfl = new DefaultFunctionLoader(this);
            string path = Utils.FunctionPath;
            if (string.IsNullOrEmpty(path))
            {
                path = Environment.CurrentDirectory;
            }

            string source = typeof (FuncNode).Assembly.Location;
            FileInfo file = new FileInfo(source);

            dfl.LoadFromFolder(file.Directory.FullName);
        }

        #region IFunctionManager Members

        /// <summary>
        /// The function collection
        /// </summary>
        public Dictionary<string, object> Functions { get; set; }

        public IFunctionLoader FunctionLoader
        {
            get { return dfl; }
        }

        /// <summary>
        /// Add a function
        /// </summary>
        /// <param name="funcName"></param>
        /// <param name="func"></param>
        public virtual void Add(string funcName, object func)
        {
            if (Functions.ContainsKey(funcName))
            {
                return;
            }

            Functions.Add(funcName, func);
        }

        /// <summary>
        /// Return the function attribute
        /// </summary>
        /// <param name="name">the name of the function</param>
        /// <returns>the function attribute</returns>
        public FunctionAttribute GetFunctionAttribute(string name)
        {
            FuncNode fn = GetFunction(name);
            if (fn == null)
            {
                return null;
            }

            return GetAttributeByNode(fn);
        }

        /// <summary>
        /// Get the designated function
        /// </summary>
        /// <param name="functionName">the function name</param>
        /// <returns>the function</returns>
        public virtual FuncNode GetFunction(string functionName)
        {
            if (Functions.ContainsKey(functionName))
            {
                object obj = Functions[functionName];
                if (obj is Type)
                {
                    var type = obj as Type;
                    return (FuncNode) type.Assembly.CreateInstance(type.FullName);
                }
                if (obj is ICloneable)
                {
                    var c = obj as ICloneable;
                    return c.Clone() as FuncNode;
                }

                return obj as FuncNode;
            }

            return null;
            /**
            switch (functionName) {
                case "sin":
                    return new SinNode();
                case "cos":
                    return new CosNode();
                case "asin":
                    return new AsinNode();
                case "acos":
                    return new AcosNode();
                case "tan":
                    return new TanNode();
                case "atan":
                    return new AtanNode();
                case "tanh":
                    return new TanhNode();
                case "atanh":
                    return new AtanhNode();
                case "abs":
                    return new AbsNode();
                case "sign":
                    return new SignNode();
                case "floor":
                    return new FloorNode();
                case "ceil":
                    return new CeilingNode();
                case "max":
                    return new MaxNode();
                case "if":
                    return new IfNode();
                case "asinh":
                    return new AsinhNode();
                case "acosh":
                    return new AcoshNode();
                case "sinh":
                    return new SinhNode();
                case "cosh":
                    return new CoshNode();
                default:
                    string config = ConfigurationManager.AppSettings[functionName.ToLower()];
                    if (string.IsNullOrEmpty(config)) {
                        return null;
                    }
                    string assemblyName = config.Split(',')[0].Trim();
                    string typeName = config.Split(',')[1].Trim();

                    FuncNode node
                        = (FuncNode)Utils.GetObject(assemblyName, typeName);
                    return node;
            }
             **/
        }

        /// <summary>
        /// Remove the designated function
        /// </summary>
        /// <param name="funcName"></param>
        public virtual void Remove(string funcName)
        {
            Functions.Remove(funcName);
        }

        /// <summary>
        /// Check if the function does already exist
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public virtual bool Exist(string fileName)
        {
            return Functions.ContainsKey(fileName);
        }


        #endregion

        /// <summary>
        /// 
        /// </summary>
        /// <param name="node"></param>
        /// <returns></returns>
        public static FunctionAttribute GetAttributeByNode(FuncNode node)
        {
            MemberInfo inf = node.GetType();

            object[] attributes = inf.GetCustomAttributes(true);
            if (attributes.Length == 0)
            {
                return null;
            }

            var attr = attributes[0] as FunctionAttribute;

            return attr;
        }

        public virtual bool IsUserDefinedFunction(string funcName)
        {
            return false;
        }
    }
}